/*
 *Copyright 2021-2023 NERCIS
 *
 *Licensed under the Apache License, Version 2.0 (the "License");
 *you may not use this file except in compliance with the License.
 *You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 *Unless required by applicable law or agreed to in writing, software
 *distributed under the License is distributed on an "AS IS" BASIS,
 *WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *See the License for the specific language governing permissions and
 *limitations under the License.
 */

package cn.ac.nercis.pes.service.project.imported;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.utils.JsonUtils;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.model.dto.project.ImportConflictDTO;
import cn.ac.nercis.pes.model.standard.asset.AssetSDTO;
import cn.ac.nercis.pes.model.standard.planning.EvaluationMethodSDTO;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetEvaluationMethodRepository;
import cn.ac.nercis.pes.repository.dal.evaluation.AssetRepository;
import cn.ac.nercis.pes.repository.dal.project.SystemTargetRepository;
import cn.ac.nercis.pes.repository.model.evaluation.AssetDO;
import cn.ac.nercis.pes.repository.model.evaluation.AssetEvaluationMethodDO;
import cn.ac.nercis.pes.repository.model.project.SystemTargetDO;
import cn.ac.nercis.pes.service.utils.AssetUtils;
import cn.ac.nercis.pes.service.utils.CheckFieldUtils;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 测评对象确定方法信息导入服务
 *
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class EvaluationMethodImportService extends CommonImportService {
    private final AssetRepository assetRepository;
    private final AssetEvaluationMethodRepository assetEvaluationMethodRepository;
    private final SystemTargetRepository systemTargetRepository;

    @Autowired
    public EvaluationMethodImportService(AssetEvaluationMethodRepository assetEvaluationMethodRepository,
                                         AssetRepository assetRepository, SystemTargetRepository systemTargetRepository) {
        this.assetEvaluationMethodRepository = assetEvaluationMethodRepository;
        this.assetRepository = assetRepository;
        this.systemTargetRepository = systemTargetRepository;
    }

    public Result<List<ImportConflictDTO>> checkEvaluationMethod(@NonNull String systemId, List<AssetSDTO> importAssets,
                                                                 List<EvaluationMethodSDTO> evaluationMethods) {
        List<AssetEvaluationMethodDO> sourceData = this.assetEvaluationMethodRepository.findAllBySystemId(systemId);
        if (CollectionUtils.isEmpty(sourceData)) {
            return Result.success();
        }
        List<AssetDO> assetDOS = this.assetRepository.findAllBySystemId(systemId);
        List<SystemTargetDO> systemTargets = this.systemTargetRepository.findAllBySystemId(systemId);
        List<ImportConflictDTO> conflictData = evaluationMethods.stream()
                .flatMap(ec -> this.checkEvaluationMethod(ec, systemTargets, sourceData,
                        findAsset(ec.getAssetId(), importAssets, assetDOS))
                        .stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        return Result.success(conflictData);
    }

    public Result<String> importEvaluationMethod(@NonNull String systemId, List<AssetSDTO> importAssets,
                                                 List<EvaluationMethodSDTO> evaluationMethods, Boolean cover) {
        List<AssetEvaluationMethodDO> sourceData = this.assetEvaluationMethodRepository.findAllBySystemId(systemId);
        List<AssetDO> assetDOS = this.assetRepository.findAllBySystemId(systemId);
        Result<String> result = Result.success();
        for (EvaluationMethodSDTO item : evaluationMethods) {
            AssetDO assetDO = findAsset(item.getAssetId(), importAssets, assetDOS);
            if (Objects.isNull(assetDO)) {
                continue;
            }
            result = this.importEvaluationMethod(systemId, item, sourceData, cover,assetDO);
            if (!result.isSuccess()) {
                break;
            }
        }
        return result;
    }

    private List<ImportConflictDTO> checkEvaluationMethod(@NonNull EvaluationMethodSDTO importData, List<SystemTargetDO> systemTargets, List<AssetEvaluationMethodDO> sourceData, AssetDO asset) {
        if (Objects.isNull(asset)) {
            return Collections.emptyList();
        }
        AssetEvaluationMethodDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getClassId(), importData.getClassId())
                        && StringUtils.equals(st.getAssetId(), asset.getId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return Collections.emptyList();
        }
        List<ImportConflictDTO> conflictData = CheckFieldUtils.checkField(entity, importData);
        if (CollectionUtils.isNotEmpty(conflictData)) {
            String dataTag = String.format("系统资产：%s <--> 测评指标：%s", AssetUtils.getAssetName(asset), getTargetName(importData.getUnitTargetId(), systemTargets));
            conflictData.add(0, ImportConflictDTO.builder()
                    .fieldName(BaseConstants.IMPORT_CONFLICT_DATA_TAG)
                    .newContent(dataTag)
                    .oldContent(dataTag)
                    .build());
        }
        return conflictData;
    }

    private Result<String> importEvaluationMethod(String systemId, @NonNull EvaluationMethodSDTO importData, List<AssetEvaluationMethodDO> sourceData, Boolean cover, AssetDO asset) {
        if (Objects.isNull(asset)) {
            log.warn("导入测评对象确定方法信息（{}）失败，未获取到指定对象信息。", JsonUtils.toJson(importData));
            return Result.failed("导入测评对象确定方法信息失败，未获取到指定对象信息。");
        }
        AssetEvaluationMethodDO entity = sourceData.stream()
                .filter(st -> StringUtils.equals(st.getClassId(), importData.getClassId())
                        && StringUtils.equals(st.getAssetId(), asset.getId())
                        && StringUtils.equals(st.getUnitTargetId(), importData.getUnitTargetId()))
                .findFirst().orElse(null);
        if (Objects.isNull(entity)) {
            return createEvaluationMethod(systemId, importData, asset.getId(), sourceData);
        } else {
            return modifyEvaluationMethod(entity, importData, cover);
        }
    }

    private Result<String> createEvaluationMethod(String systemId, EvaluationMethodSDTO importData, String assetId,
                                                  List<AssetEvaluationMethodDO> sourceData) {
        AssetEvaluationMethodDO entity = AssetEvaluationMethodDO.builder()
                .systemId(systemId)
                .classId(importData.getClassId())
                .assetId(assetId)
                .unitTargetId(importData.getUnitTargetId())
                .layerTargetId(importData.getLayerTargetId())
                .method(importData.getMethod())
                .desc(importData.getDesc())
                .unitTargetSort(importData.getUnitTargetSort())
                .finishState(importData.getFinishState())
                .build();
        entity.setCreateTime(LocalDateTime.now());
        return Optional.of(entity)
                .map(this.assetEvaluationMethodRepository::save)
                .map(em->{
                    sourceData.add(em);
                    return Result.success(em.getId());
                })
                .orElse(Result.failed("导入测评对象确定方法信息，创建失败"));
    }

    private Result<String> modifyEvaluationMethod(AssetEvaluationMethodDO sourceData, EvaluationMethodSDTO importData, Boolean cover) {
        sourceData.setMethod(CheckFieldUtils.applyContent(sourceData.getMethod(), importData.getMethod(), cover));
        sourceData.setDesc(CheckFieldUtils.applyContent(sourceData.getDesc(), importData.getDesc(), cover));
        sourceData.setFinishState(CheckFieldUtils.applyContent(sourceData.getFinishState(), importData.getFinishState(), cover));
        sourceData.setModifyTime(LocalDateTime.now());
        return Optional.of(sourceData)
                .map(this.assetEvaluationMethodRepository::save)
                .map(AssetEvaluationMethodDO::getId)
                .map(Result::success)
                .orElse(Result.failed("导入测评对象确定方法信息，修改失败"));
    }
}
